<?php


namespace App\Web\Model\Redis;


use App\Exception\ExcludedException;
use App\Exception\NoSessionException;
use App\Exception\RightChangeException;
use App\Facades\JWTAuth;
use EchoPHP\Facades\Redis;
use EchoPHP\Facades\Request;
use Ramsey\Uuid\Uuid;

class LoginSession {

    /**
     * redis客户端实例
     * @var \Redis
     */
    private $client;
    private $refreshTokenExpire;
    private $authTokenExpire;
    private $sessionExpire;

    public function __construct() {
        $this->client = Redis::connect('login_session');

        $this->refreshTokenExpire = config('app.tokenexpire.refresh_token', 120);
        $this->authTokenExpire = config('app.tokenexpire.auth_token', 604800);
        $this->sessionExpire = config('app.tokenexpire.session', 1440);
    }

    /**
     * 获取真实键名
     * @param $key
     * @return string
     */
    public function getKey($key) {
        return 'loginsession:' . $key;
    }

    /**
     * 生成兑换token
     * @param $userId
     * @param $userName
     * @return string
     */
    public function generateRefreshToken($userId, $userName) {
        $jwtInfo = [
            'userid' => $userId,
            'username' => $userName,
            'ip' => Request::instance()->getClientIp(),
        ];
        return JWTAuth::encode($jwtInfo, $this->refreshTokenExpire);
    }

    /**
     * 判断登录会话是否存在
     * @param $userId
     * @return bool
     */
    public function hasSession($userId) {
        return $this->client->exists($this->getKey($userId)) > 0;
    }

    /**
     * 解析兑换码
     * @param $refreshToken
     * @return array|bool
     */
    public function chkRefreshToken($refreshToken) {
        try {
            $jwtInfo = JWTAuth::decode($refreshToken);
        } catch (\Exception $e) {
            return false;
        }
        //ip地址发生变化，refreshToken也认为失效
        if ($jwtInfo['ip'] != Request::instance()->getClientIp()) {
            return false;
        }
        return $jwtInfo;
    }

    /**
     * 设置登录会话
     * @param $loginInfo
     * @return bool
     * @throws \Exception
     */
    public function setSession($loginInfo) {
        $token = Uuid::uuid1()->toString();
        $clientIp = Request::instance()->getClientIp();
        $loginInfo['token'] = $token;
        $loginInfo['ip'] = $clientIp;
        $loginInfo['createat'] = time();

        $authInfo = [
            'userid' => $loginInfo['userid'],
            'username' => $loginInfo['username'],
            'cname' => $loginInfo['cname'],
            'ri' => $loginInfo['rights'],
            'issuper' => $loginInfo['issuper'] == 1 ? 1 : 0,
            'isleader' => $loginInfo['teamrole'] == 2 ? 1 : 0,
            'team' => $loginInfo['teamcode'],
            'jti' => $token,
            'ip' => $clientIp,
        ];
        $authToken = JWTAuth::encode($authInfo, $this->authTokenExpire);

        if ($this->client->set($this->getKey($loginInfo['userid']), json_encode($loginInfo), $this->sessionExpire)) {
            return $authToken;
        }
        return false;
    }

    /**
     * 刷新authToken
     * @param $userId
     * @return string
     */
    public function refreshAuthToken($userId) {
        $sessionToken = $this->client->get($this->getKey($userId));
        if (!$sessionToken) {
            return false;
        }
        $sessionInfo = json_decode($sessionToken, true);

        $authInfo = [
            'userid' => $sessionInfo['userid'],
            'username' => $sessionInfo['username'],
            'cname' => $sessionInfo['cname'],
            'ri' => $sessionInfo['rights'],
            'jti' => $sessionInfo['token'],
            'ip' => Request::instance()->getClientIp(),
        ];
        return JWTAuth::encode($authInfo, $this->authTokenExpire);
    }

    /**
     * 刷新登录会话信息
     * @param $loginInfo
     * @param null $token
     * @return bool
     * @throws \Exception
     */
    public function refreshSession($loginInfo, $token = null) {
        if (is_null($token)) {
            $token = Uuid::uuid1()->toString();
        }

        $clientIp = Request::instance()->getClientIp();
        $loginInfo['token'] = $token;
        $loginInfo['ip'] = $clientIp;
        $loginInfo['createat'] = time();
        if ($this->client->set($this->getKey($loginInfo['userid']), json_encode($loginInfo), $this->sessionExpire)) {
            return $loginInfo;
        }
        return false;
    }

    /**
     * 清除登录会话
     * @param $userId
     * @return int
     */
    public function clearSession($userId) {
        if (false !== ($info = $this->client->get($this->getKey($userId)))) {
            $this->client->del($this->getKey($userId));
            return json_decode($info, true);
        }
        return false;
    }

    /**
     * 校验会话
     * @param $authToken
     * @return array|bool
     */
    public function chkSession($authToken) {
        $authInfo = JWTAuth::decode($authToken);
        $sessionToken = $this->client->get($this->getKey($authInfo['userid']));
        if (!$sessionToken) {
            $message = $this->flashMsg($authInfo['jti']);
            throw new NoSessionException($message);
        }
        $sessionInfo = json_decode($sessionToken, true);
        if (!$sessionInfo) {
            throw new ExcludedException('登录令牌不一致');
        }
        if ($sessionInfo['token'] != $authInfo['jti'] && env('APP_ENV') != 'develop') {
            throw new ExcludedException('登录令牌不一致');
        }
        if ($sessionInfo['rights'] != $authInfo['ri']) {
            throw new RightChangeException('权限已变化');
        }
        //如果相同，延长会话有效期
        $this->client->expire($this->getKey($authInfo['userid']), $this->sessionExpire);
        return $sessionInfo;
    }

    /**
     * 设置session消息
     * @param $sessionId
     * @param $message
     */
    public function sessionMsg($sessionId, $message) {
        $key = 'sessionmsg:' . $sessionId;
        $this->client->set($key, $message, $this->sessionExpire);
    }

    /**
     * 获取并清除session消息
     * @param $sessionId
     * @return string
     */
    public function flashMsg($sessionId) {
        $key = 'sessionmsg:' . $sessionId;
        $message = $this->client->get($key);
        $this->client->del($key);
        return (string)$message;
    }

}